package icu.zhhll.datasource.dynamic;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.util.ClassUtils;
import org.springframework.validation.DataBinder;

import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;

/**
 * 数据源构造器
 *
 * @author zh
 * @date 2023/10/7 17:22
 */
public final class DataSourceBuilder<T extends DataSource> {
    private final Logger LOGGER = LoggerFactory.getLogger(DynamicDataSource.class);

    private static final String[] DATA_SOURCE_TYPE_NAMES = new String[]{"com.zaxxer.hikari.HikariDataSource",
            "org.apache.tomcat.jdbc.pool.DataSource", "org.apache.commons.dbcp2.BasicDataSource",
            "com.alibaba.druid.pool.DruidDataSource"};

    private Class<? extends DataSource> type;

    private final ClassLoader classLoader;

    private final Map<String, String> properties = new HashMap<>();

//    private Properties extraProps;

    private Map<String, Object> extraProps;

    public static DataSourceBuilder<?> create() {
        return new DataSourceBuilder<>(null);
    }

    public static DataSourceBuilder<?> create(ClassLoader classLoader) {
        return new DataSourceBuilder<>(classLoader);
    }

    private DataSourceBuilder(ClassLoader classLoader) {
        this.classLoader = classLoader;
    }

    @SuppressWarnings("unchecked")
    public T build() {
        Class<? extends DataSource> type = getType();
        DataSource result = BeanUtils.instantiateClass(type);
        // 进行参数绑定
        DataBinder dataBinder = new DataBinder(result);

        if (type.getName().equals("com.zaxxer.hikari.HikariDataSource")) {
            properties.put("jdbcUrl", properties.get("url"));
        }
        dataBinder.bind(new MutablePropertyValues(properties));

        if (extraProps != null) {
            dataBinder.bind(new MutablePropertyValues(extraProps));
        }

        return (T) result;
    }

    public DataSourceBuilder<T> extraProp(Map<String, Object> extraProps) {
        this.extraProps = extraProps;
        return this;
    }

    @SuppressWarnings("unchecked")
    public <D extends DataSource> DataSourceBuilder<D> type(Class<D> type) {
        this.type = type;
        return (DataSourceBuilder<D>) this;
    }

    public DataSourceBuilder<T> url(String url) {
        this.properties.put("url", url);
        return this;
    }

    public DataSourceBuilder<T> driverClassName(String driverClassName) {
        this.properties.put("driverClassName", driverClassName);
        return this;
    }

    public DataSourceBuilder<T> username(String username) {
        this.properties.put("username", username);
        return this;
    }

    public DataSourceBuilder<T> password(String password) {
        this.properties.put("password", password);
        return this;
    }

    @SuppressWarnings("unchecked")
    public static Class<? extends DataSource> findType(ClassLoader classLoader) {
        for (String name : DATA_SOURCE_TYPE_NAMES) {
            try {
                return (Class<? extends DataSource>) ClassUtils.forName(name, classLoader);
            } catch (Exception ex) {
                // Swallow and continue
            }
        }
        return null;
    }

    private Class<? extends DataSource> getType() {
        Class<? extends DataSource> type = (this.type != null) ? this.type : findType(this.classLoader);
        if (type != null) {
            return type;
        }
        throw new IllegalStateException("没有找到支持的连接池类型");
    }

}
